package org.wappumobileapp;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;

import com.thoughtworks.xstream.XStream;

import dataobjects.AddMessage;
import dataobjects.Category;
import dataobjects.DeleteQuery;
import dataobjects.DislikeQuery;
import dataobjects.HistoryQuery;
import dataobjects.IndifferentQuery;
import dataobjects.LikeQuery;
import dataobjects.Message;
import dataobjects.MessageQuery;
import dataobjects.Query;
import dataobjects.ReportQuery;

public class GetFromService {

	// static HttpClient client;
	public static List<Long> alreadyReadMessages = new ArrayList<Long>();
	static long latestTimestamp = Long.MIN_VALUE;
	static long oldestTimestamp = Long.MAX_VALUE;
	static Map<Long, List<Message>> messagesByCategory = new HashMap<Long, List<Message>>();

	static List<ServerResponceListener> listeners = new LinkedList<ServerResponceListener>();

	public static void addServerResponceListener(ServerResponceListener listener) {
		listeners.add(listener);

	}

	public static void removeServerResponceListener(
			ServerResponceListener listener) {
		listeners.remove(listener);
	}

	private GetFromService() {

	}

	public static void indifferentMessage(Message m) {

		messagesByCategory.get(m.getCategoryID()).remove(m);
		IndifferentQuery like = new IndifferentQuery(m.getId());
		backgroundExecuteQuery(like);
	}

	public static void likeMessage(Message m) {
		messagesByCategory.get(m.getCategoryID()).remove(m);
		LikeQuery like = new LikeQuery(m.getId());
		backgroundExecuteQuery(like);
	}

	public static void dislikeMessage(Message m) {
		messagesByCategory.get(m.getCategoryID()).remove(m);
		Query like = new DislikeQuery(m.getId());
		backgroundExecuteQuery(like);

	}

	public static Message deleteMessage(Message m) {
		Query delete = new DeleteQuery(m.getId());
		return executeLike(delete);
	}

	private static Message executeLike(Query like) {
		try {
			HttpClient client = new DefaultHttpClient();
			HttpPost request = new HttpPost(
					"http://23.21.255.34/httpservice/api");
			putObjectIntoHttpRequest(request, like);

			HttpResponse response = client.execute(request);

			Message m = (Message) getObjectFromHttpResponce(response);

			fireLikeSuccessful(m);
			return m;

		} catch (Exception e) {
			fireServerDownError();
		}

		return null;
	}

	private static void fireServerDownError() {
		for (ServerResponceListener l : listeners) {
			l.handleError("Service is down");

		}
	}

	private static void fireLikeSuccessful(Message m) {
		for (ServerResponceListener l : listeners) {
			l.likeSuccessful(m);

		}

	}

	public static Message reportMessage(Message m) {
		messagesByCategory.get(m.getCategoryID()).remove(m);
		
		HttpClient client = new DefaultHttpClient();
		HttpPost request = new HttpPost("http://23.21.255.34/httpservice/api");
		ReportQuery like = new ReportQuery(m.getId());

		messagesByCategory.get(m.getCategoryID()).remove(m);
		try {
			putObjectIntoHttpRequest(request, like);

			HttpResponse response = client.execute(request);

			return (Message) getObjectFromHttpResponce(response);

		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return m;

	}

	// apumetodi t�h�n v�liin

	public static Message addSuggestion(String message, String nick,
			Category category, Long latitude, Long longitude) {
		HttpClient client = new DefaultHttpClient();
		HttpPost request = new HttpPost("http://23.21.255.34/httpservice/api");
		AddMessage addMessage = new AddMessage(nick, message, latitude,
				longitude, category.getID());
		Message fromService = null;
		try {
			putObjectIntoHttpRequest(request, addMessage);

			HttpResponse response = client.execute(request);

			Object objectFromHttpResponce = getObjectFromHttpResponce(response);
			fromService = (Message) objectFromHttpResponce;

		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return fromService;
	}

	/**
	 * Use this to read messages of category. The idea is that this handles
	 * caching with like/dislike/indifferent.
	 * 
	 * @param category
	 * @return
	 */
	public static Message readMessage(final Category category) {
		List<Message> messagesOfCategory = messagesByCategory.get(category
				.getID());

		if (messagesOfCategory != null && messagesOfCategory.size() > 0) {
			if (messagesOfCategory.size() < 10) {
				backgroundExecuteRead(category);
			}
			return messagesOfCategory.get(0);
		} else {
			// TODO should we try to get more messages one more time?
			return null;
		}
	}

	/**
	 * Use this to init service
	 */
	public static void init() {
		getMessages(null);
	}

	/**
	 * Used internally, please check readMessage(Category cat) for reading
	 * message of a category.
	 * 
	 * @param category
	 * @return
	 */
	private static void getMessages(Category category) {
		try {

			HttpClient client = new DefaultHttpClient();
			HttpPost request = new HttpPost(
					"http://23.21.255.34/httpservice/api");
			MessageQuery query = new MessageQuery();
			if (alreadyReadMessages != null && alreadyReadMessages.size() > 0) {
				query.setAlreadyReadMessages(alreadyReadMessages);
			}
			if (category != null)
				query.setCategory(category.getID());

			putObjectIntoHttpRequest(request, query);

			HttpResponse response = client.execute(request);

			Object o = getObjectFromHttpResponce(response);

			List<Message> messages = o instanceof List ? (List<Message>) o
					: new ArrayList<Message>();
			for (Message m : messages) {
				if (m != null) {
					alreadyReadMessages.add(m.getId());
					long id = m.getCategoryID();
					if (!messagesByCategory.containsKey(id)) {
						messagesByCategory.put(id, new ArrayList<Message>());
					}

					List<Message> list = messagesByCategory.get(id);
					list.add(m);

				}

			}

		} catch (Exception e) {
			// POPUP here
			// TODO create intent to open main with an error
			e.printStackTrace();

		}

	}

	/*
	 * 
	 */
	public static List<Message> getHistory(List<Long> idList) {
		if (idList == null)
			return Collections.EMPTY_LIST; // No need to call server

		try {

			HttpClient client = new DefaultHttpClient();
			HttpPost request = new HttpPost(
					"http://23.21.255.34/httpservice/api");
			HistoryQuery query = new HistoryQuery(idList);
			putObjectIntoHttpRequest(request, query);

			HttpResponse response = client.execute(request);

			Object o = getObjectFromHttpResponce(response);

			List<Message> messages = o instanceof List ? (List<Message>) o
					: new ArrayList<Message>();

			return messages;
		} catch (Exception e) {

			e.printStackTrace();
			return null;
		}

	}

	public static Object getObjectFromHttpResponce(HttpResponse response)
			throws IOException {
		InputStream stream = response.getEntity().getContent();
		XStream xstream = new XStream();
		Object o = xstream.fromXML(stream);
		return o;
	}

	public static void putObjectIntoHttpRequest(HttpPost request, Object object)
			throws UnsupportedEncodingException {
		XStream xstream = new XStream();
		String entity = xstream.toXML(object);
		request.setEntity(new StringEntity(entity));
	}

	private static Map<Category, Boolean> queries = new HashMap<Category, Boolean>();

	final static LinkedList<Runnable> queue = new LinkedList<Runnable>();
	// Ugly first version
	static Thread worker;

	private static void backgroundExecuteRead(final Category c) {

		Runnable r = new Runnable() {
			public void run() {
				getMessages(c);
				queries.put(c, false);

			}

		};
		synchronized (queue) {
			if (queries.get(c) != null && queries.get(c)) {
				return;
			} else {
				queries.put(c, true);
			}

			queue.addFirst(r);
			queue.notifyAll();

		}

	}

	private static void backgroundExecuteQuery(final Query like) {

		Runnable r = new Runnable() {
			public void run() {
				executeLike(like);

			}

		};
		synchronized (queue) {
			queue.addLast(r);
			queue.notifyAll();

		}

	}

	static {
		worker = new Thread() {

			public void run() {
				while (true) {
					Runnable r;
					synchronized (queue) {
						if (queue.isEmpty()) {
							try {
								queue.wait();
							} catch (InterruptedException e) {

								e.printStackTrace();
							}
						}
						r = queue.remove();
					}
					r.run();
				}
			}
		};

		worker.start();
	}

	public static void clear() {
		alreadyReadMessages.clear(); // TODO this clear can be removed?
		messagesByCategory.clear();

	}
}
